function parseSize(sizeStr){
	if(!sizeStr || sizeStr.length == 0)
		return null;
	sizeStr = sizeStr.replace(/(\s|,)+/g, '');
	var lastChar = sizeStr.charAt(sizeStr.length-1);
	var a = 1;
	if(lastChar<'0' || lastChar>'9'){
		switch(lastChar)
		{
			default:
				return Number.NaN;
			case 'g':
			case 'G':
				a *= 1024;
			case 'm':
			case 'M':
				a *= 1024;
			case 'k':
			case 'K':
				a *= 1024;
		}
		sizeStr = sizeStr.substr(0,sizeStr.length-1);
	}
	if(/^\d+$/.test(sizeStr))
		return a * parseInt(sizeStr);
	return Number.NaN;
}

function formatSize(size){
	if(!size || size != size)
		return '';
	var s = ['', 'K', 'M', 'G'];
	for(var i=0; i<4; i++){
		if(size<1024 || i==3)
			return Math.round(size) + s[i];
		size /= 1024;
	}
}

function parseTime(timeStr){
	return timeStr;
}
function parseType(typeStr){
	return typeStr;
}

var TextExParser = $C({
	prototype:{
		initialize:function(leftFragment, rightFragment, option){
			this.leftFragment = leftFragment;
			this.rightFragment = rightFragment;
			this.option = (option.indexOf('m')>=0?'m':'') + (option.indexOf('i')>=0?'i':'');
			this.leftRegExp = new RegExp(leftFragment,this.option);
			this.rightRegExp = new RegExp(rightFragment,this.option);
		},

		//return a array of string:[outside string, inside string, out..., in..., ...]
		parse: function(text){
			var ret = [], a;
			while(true){
				a = text.match(this.leftRegExp);
				if(!a)
					break;
				ret.push(text.substr(0,a.index));
				text = text.substr(a.index+a[0].length);
				a = text.match(this.rightRegExp);
				if(!a)
					break;
				ret.push(text.substr(0,a.index));
				text = text.substr(a.index+a[0].length);
			}
			ret.push(text);
			ret.format = function(prefix, suffix){
				var s = '';
				for(var i=0; i<this.length; i++){
					s += this[i];
					if(++i>=this.length)
						break;
					s += prefix;
					s += this[i];
					s += suffix;
				}
				return s;
			};
			ret.toString = function(){
				var s = '';
				this.each(function(it){s+=it});
				return s;
			}
			return ret;
		}
	}
});

var SE = $C({
	Version: '0.0.1',
	All:{},
	prototype:{
		toString: function(){return this.name;},
		initialize: function(name, options) {
			this.name = name = name || 'se';
			if(SE.All[name] !== undefined)
				throw new Error("There is already a SE '" + name + "' in the set.");
			SE.All[name] = this;
			this.options = Object.extend({
				getURL:					function(wd,rn,pn){return 'wd='+wd+'&rn='+rn+'&pn='+pn;},
				getTextFromTransport:	function(trans){return trans.responseText;},
				checkItem:				function(item){return true;},
				encoding:				'gb2312',
				sectionFragment:		'^(\n|.)*$',
				totalNumberGI:			null,
				itemsGI:				null,
				nextGI:					null,
				itemFragment:			'(?=\s*)\w+(?=\s*)',
				typeGI:					null,
				linkGI:					null,
				titleGI:				null,
				detailGI:				null,
				sizeGI:					null,
				timeGI:					null,
				cacheLinkGI:			null,
				parseType:				parseType,
				parseSize:				parseSize,
				parseTime:				parseTime,
				highlightFragment1:		null,
				highlightFragment2:		null
			}, options || {});
			options = this.options;
			var textExParser = Runner.through(
				new TextExParser(options.highlightFragment1, options.highlightFragment2, 'm').parse, 
				function(a){
					if(a == null)
						return null;
					a.each(function(v,i){
						a[i] = v.stripTags();
					});
					return a;
				}
				);
			var checkItem = this.checkItem;
			var URLctor = $ctor(URL);
			this.parse = Runner.through(
				$apply(''.stripScripts),
				new Parser(options.sectionFragment, 'm',{
					totalNumber:	[options.totalNumberGI, parseSize],
					next:			options.nextGI,
					items:			[options.itemsGI, Runner.through(
						new Parser(options.itemFragment, 'gm', {
							se:				Runner.doreturn(this),
							type:			[options.typeGI,options.parseType], 
							link:			[options.linkGI, URLctor],
							title:			[options.titleGI, textExParser],
							detail:			[options.detailGI, textExParser],
							size:			[options.sizeGI, options.parseSize],
							time:			[options.timeGI, options.parseTime],
							cacheLink:		[options.cacheLinkGI, URLctor]
						}).parse, 
						function(items){items.each(function(it,i){if(!checkItem(it))items[i] = null;});return items;}
					)]
				}).parse
				);
		},

		// wd: query word, rn: items number of one page, pn: position(default 0 is the first)
		getURL: function(wd,rn,pn){
			pn = pn || 0;
			if(!wd || (typeof(wd) != 'string') || !rn || (typeof(rn) != 'number') || (typeof(pn) != 'number'))
				return null;
			return this.options.getURL(wd, rn, pn);
		},

		getTextFromTransport: function(trans){
			return this.options.getTextFromTransport(trans);
		},

		checkItem: function(item){
			return !!item.link && !!item.title && this.options.checkItem(item)
		}
	}
});

function $SE(se){
	if (arguments.length != 1) {
		var ses = [];
		if(arguments.length > 1){
			$A(arguments).each(function(v){
				ses.push($SE(v));
			});
			return ses;
		}
		for(se in SE.All)
			ses.push($SE(se));
		return ses;
	}
	if (typeof se == 'string')
		se = SE.All[se];
	if(se instanceof SE)
		return se;
	return null;
};
